﻿using System;
using UnityEngine;

namespace Core
{
    public struct VisualParam
    {
        public string Path;
        public float OffsetY;
        public int SortingOrder;
        public Sprite Icon;
        public GameObject Parent;
    }

    /// <summary>
    /// 单Render 模型组件
    /// </summary>
    [AllowMultiple(true)]
    public class VisualComponent : Core.Component, IGameObject,
        IAwake<string>, IAwake<VisualParam>, IAwake<VisualParam, float>
    {
        public GameObject gameObject { get; private set; }
        public Transform transform { get; private set; }
        public bool IsLoadComplete { get; private set; }
        public SpriteRenderer Render { get; private set; }

        public float Scale { get; private set; }
        private event Action OnLoaded;

        private IGameObject unit;
        private VisualParam param;


        public void Awake(string path)
        {
            Scale = 1;
            Awake(new VisualParam { Path = path, OffsetY = 0, SortingOrder = 0 });
        }

        public void Awake(VisualParam param)
        {
            Awake(param, 1);
        }

        public void Awake(VisualParam param, float scale)
        {
            Scale = scale;
            this.param = param;
            SortingOrder = param.SortingOrder;
            Sprite = param.Icon;

            Name = "model";

            unit = Entity as IGameObject;

            if (unit == null)
            {
                log.err($"ModelComponent 需要挂载在 IGameObject对象上");
            }

            IsLoadComplete = false;
            Load().Coroutine();
        }

        private void Awake(GameObject go)
        {
            IsLoadComplete = true;
            gameObject = go;
            transform = gameObject.transform;

            InitRender();

            if (param.Parent == null)
            {
                transform.SetParent(unit);
                transform.SetLayer(unit.gameObject.layer);
            }
            else
            {
                transform.SetParent(param.Parent.transform);
                transform.SetLayer(param.Parent.layer);
            }

            transform.Reset();

            transform.SetLocalPosY(param.OffsetY);
            transform.localScale = Vector3.one * Scale;
        }

        public override void Dispose()
        {
            if (IsDisposed) return;
            base.Dispose();

            mSortingOrder = 0;
            mSprite = null;

            transform = null;
            Utils.Trans.Destroy(gameObject);
            gameObject = null;
        }

        private void InitRender()
        {
            Render = gameObject.GetComponent<SpriteRenderer>();
            if (Render != null)
            {
                if (cacheSortingOrder)
                {
                    Render.sortingOrder = mSortingOrder;
                }
                else
                {
                    mSortingOrder = Render.sortingOrder;
                }

                if (mSprite != null)
                {
                    Render.sprite = mSprite;
                }
                else
                {
                    mSprite = Render.sprite;
                }
            }
        }

        private bool cacheSortingOrder;
        private int mSortingOrder;

        public int SortingOrder
        {
            get => mSortingOrder;
            set
            {
                cacheSortingOrder = true;
                mSortingOrder = value;
                if (Render != null)
                {
                    Render.sortingOrder = value;
                }
            }
        }

        private Sprite mSprite;

        public Sprite Sprite
        {
            get => mSprite;
            set
            {
                if (value == mSprite)
                {
                    return;
                }

                mSprite = value;
                if (Render != null)
                {
                    Render.sprite = value;
                }
            }
        }


        private async ETVoid Load()
        {
            var asset = await G.Res.LoadAsync(param.Path);

            var go = UnityEngine.Object.Instantiate(asset.Obj) as GameObject;

            Awake(go);

            try
            {
                OnLoaded?.Invoke();
            }
            catch (Exception e)
            {
                log.err(e);
            }

            IsLoadComplete = true;
        }

        public void AddLoadListener(Action func)
        {
            if (IsLoadComplete == false)
            {
                OnLoaded += func;
            }
            else
            {
                func();
            }
        }

        public void RemoveLoadListener(Action func)
        {
            OnLoaded -= func;
        }
    }
}